#ifndef SDFS_SERVER_H
#define SDFS_SERVER_H

#include <muduo/base/Atomic.h>
#include <muduo/base/Logging.h>
#include <muduo/base/Thread.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpServer.h>
#include <muduo/net/InetAddress.h>

#include <boost/bind.hpp>

#include <sdfs/protobuf/Message.h>
#include <sdfs/config/TrackerConfig.h>
#include <sdfs/protobuf/Codec.h>
#include <sdfs/protobuf/ProtobufDispatcher.h>
#include <sdfs/protobuf/group.pb.h>
#include <sdfs/redis/RedisConnector.h>
#include <sdfs/tracker/StorageServerProxy.h>

using namespace muduo::net;

using namespace sdfs::protobuf;

using namespace sdfs::redis;

using namespace sdfs::protobuf::file;

namespace sdfs
{

namespace tracker
{

class TrackerServer
{
public:
	TrackerServer(EventLoop* loop, const InetAddress& addr, int threadNum)
		:server_(loop, addr, sdfs::TrackerConfig::name()),
		fileserver_(loop, addr, "FileDataServer"), 
		 dispatcher_(
		 	boost::bind(&TrackerServer::discardMessage, this, _1, _2, _3)
		 ),
		 codec_(
		 	boost::bind(&ProtobufDispatcher::onProtobufMessage, &dispatcher_, _1, _2, _3)
		 ),
		 redisConn_(loop, sdfs::TrackerConfig::redisIp().c_str(), sdfs::TrackerConfig::redisPort()),
		 startTime_(Timestamp::now()),
		 threadNum_(threadNum)
	{
		server_.setConnectionCallback(
			boost::bind(&TrackerServer::onConnection, this, _1));
		server_.setMessageCallback(
			boost::bind(&ProtobufCodec::onMessage, &codec_, _1, _2, _3));

		fileserver_.setMessageCallback(
			)
		dispatcher_.registerProtobufMessageCallback<sdfs::protobuf::file::FileDataMessage>(
			boost::bind(&TrackerServer::onRecvFile, this, _1, _2, _3));
	}

	void onGroupSeek(const muduo::net::TcpConnectionPtr& conn,
		const shared_ptr<sdfs::protobuf::tracker::GroupSeeker> message, muduo::Timestamp timestamp)
	{	
		LOG_DEBUG << "[" << conn->peerAddress().toIpPort() << "] seeks for service";

		shared_ptr< std::vector<ServerStatusPtr> > storages = StorageServerProxy::packageStorageGroup();

		sdfs::protobuf::tracker::GroupPackage responseMessage;
		responseMessage.set_id(curId_.addAndGet(1));
		responseMessage.set_reqid(message->id());
		if(storages->size() < 1)
		{
			responseMessage.set_result(sdfs::protobuf::tracker::GroupPackage::REFUSE);
			responseMessage.set_reason("No Storage Service found");
		}
		else
		{
			responseMessage.set_result(sdfs::protobuf::tracker::GroupPackage::ACCEPT);
			// char ip[32];
			for (uint i = 0; i < storages->size(); ++i)
			{
				/* code */
				
				// sdfs::ToolKit::toIp(ip, 32, storages->at(i).getSockAddrInet());
				// int port = sdfs::ToolKit::toPort(storages->at(i).getSockAddrInet());
				sdfs::protobuf::tracker::GroupPackage_Worker* si = responseMessage.add_worker();
				si->set_ip(storages->at(i)->getIp().c_str());
				si->set_port(storages->at(i)->getPort());
			}
		}
		
		codec_.Send(conn, responseMessage);
	}

	

	void discardMessage(const TcpConnectionPtr& conn, 
		const MessagePtr message, Timestamp timestamp)
	{
		LOG_TRACE << "message["<<message->GetTypeName()
			<< "] from: " << conn->peerAddress().toIpPort()
			<< " discard";
	}

	void onConnection(const TcpConnectionPtr& conn)
	{
		LOG_DEBUG << conn->peerAddress().toIpPort() <<
			" -> " << conn->localAddress().toIpPort() <<
			" is " << (conn->connected() ? "UP" : "DOWN");
	}

	void start()
	{
		LOG_DEBUG << "starting tracker server";
		server_.start();
	}

	/* data */
private:
	TcpServer server_;
	TcpServer fileserver_;
	ProtobufDispatcher dispatcher_;
	ProtobufCodec codec_;
	RedisConnector redisConn_;
	Timestamp startTime_;
	int threadNum_;
	AtomicInt32 curId_;

	
};
}
}

#endif
